﻿using System;
using System.Data.SqlClient;
using System.Data;
using System.Text;
using System.Security.Cryptography;
using System.Text.RegularExpressions;

namespace Hanhud.Security
{
    // Summary:
    //     Describes the result of a Hanhud.Security.Membership.CreateUser(System.String,System.String)
    //     operation.
    public enum EMembershipCreateStatus
    {
        // Summary:
        //     The user was successfully created.
        Success = 0,
        //
        // Summary:
        //     The user name was not found in the database.
        InvalidUserName = 1,
        //
        // Summary:
        //     The password is not formatted correctly.
        InvalidPassword = 2,
        //
        // Summary:
        //     The password question is not formatted correctly.
        InvalidQuestion = 3,
        //
        // Summary:
        //     The password answer is not formatted correctly.
        InvalidAnswer = 4,
        //
        // Summary:
        //     The e-mail address is not formatted correctly.
        InvalidEmail = 5,
        //
        // Summary:
        //     The user name already exists in the database for the application.
        DuplicateUserName = 6,
        //
        // Summary:
        //     The e-mail address already exists in the database for the application.
        DuplicateEmail = 7,
        //
        // Summary:
        //     The user was not created, for a reason defined by the provider.
        UserRejected = 8,
        //
        // Summary:
        //     The provider user key is of an invalid type or format.
        InvalidProviderUserKey = 9,
        //
        // Summary:
        //     The provider user key already exists in the database for the application.
        DuplicateProviderUserKey = 10,
        //
        // Summary:
        //     The provider returned an error that is not described by other System.Web.Security.MembershipCreateStatus
        //     enumeration values.
        ProviderError = 11,
    }
}
namespace Hanhud.Data{

    namespace Providers
    {
        using Hanhud.Security;
        using Hanhud.Pattern;
        
        internal class CMembershipProvider
        {
            private CDbAccess _db = null;
            //private static int _passwordSize = 14;

            public static CMembershipProvider Instance
            {
                get
                {
                    return CPageSingleton<CMembershipProvider>.Instance;
                }
            }
                 

            #region Fields
            internal bool EnablePasswordReset { get; private set; }
            internal bool EnablePasswordRetrieval { get; private set; }
            internal int MaxInvalidPasswordAttempts { get; private set; }
            internal int MinRequiredNonAlphanumericCharacters { get; private set; }
            internal int MinRequiredPasswordLength { get; private set; }
            internal string PasswordStrengthRegularExpression { get; private set; }
            internal bool RequiresUniqueEmail { get; private set; }
            internal bool RequiresQuestionAndAnswer { get; private set; }
            internal int PasswordAttemptWindow { get; private set; }
            internal bool UseSalt { get; private set; }
            internal string HashCase { get; private set; }
            internal bool HashHex { get; private set; }
            internal string HashRemoveChars { get; private set; }
            #endregion

            public CMembershipProvider()
            {
                _db = new CDbAccess(Core.CBaseConfig.Current.DbSchema, "Membership_");
                this.UseSalt = false;
                this.HashHex = true;
                this.HashCase = "upper";
                this.HashRemoveChars = string.Empty;

                _db.SetConnectionManagerAdapter<CMembershipProvider>();
            }

            #region Public Methods
            internal bool ChangePassword(string username, string oldPassword, string newPassword)
            {
                bool success = false;

                string passwordSalt = string.Empty;
                string newEncPassword = string.Empty;
                // Clean input

                // Check password meets requirements as set by Configuration settings
                if (!IsPasswordCompliant(newPassword))
                {
                    return false;
                }

                return success;
            }

            internal bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
            {
                bool success = false;

                SqlCommand cmd = new SqlCommand(_db.GetObjectName("ChangePasswordQuestionAndAnswer"));
                cmd.CommandType = CommandType.StoredProcedure;

                cmd.Parameters.Add("@UserName", SqlDbType.NVarChar).Value = username;
                cmd.Parameters.Add("@Password", SqlDbType.NVarChar).Value = CMembershipProvider.Encrypt(password, CMembershipProvider.GenerateSalt(), true);
                cmd.Parameters.Add("@PasswordQuestion", SqlDbType.NVarChar).Value = newPasswordQuestion;
                cmd.Parameters.Add("@PasswordAnswer", SqlDbType.NVarChar).Value = newPasswordAnswer;

                try
                {
                    _db.ExecuteNonQuery(cmd);
                    success = true;
                }
                catch (Exception ex) { string msg = ex.Message; success = false; }

                return success;
            }

            internal CMembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, out EMembershipCreateStatus createStatus)
            {
                string salt = string.Empty, pass = string.Empty;
                // Check password meets requirements as set out in the web.config
                if (!IsPasswordCompliant(password))
                {
                    createStatus = EMembershipCreateStatus.InvalidPassword;
                    return null;
                }

                // Check password Question and Answer requirements.
                if (RequiresQuestionAndAnswer)
                {
                    if (String.IsNullOrEmpty(passwordQuestion))
                    {
                        createStatus = EMembershipCreateStatus.InvalidQuestion;
                        return null;
                    }

                    if (String.IsNullOrEmpty(passwordAnswer))
                    {
                        createStatus = EMembershipCreateStatus.InvalidAnswer;
                        return null;
                    }
                }

                // Check for unique email
                if (RequiresUniqueEmail)
                {
                    if (!String.IsNullOrEmpty(GetUserNameByEmail(email)))
                    {
                        createStatus = EMembershipCreateStatus.DuplicateEmail; // Email exists
                        return null;
                    }
                }

                // Check for unique user roomID
                if (!(GetUser(username, false) == null))
                {
                    createStatus = EMembershipCreateStatus.DuplicateUserName; // Username exists
                    return null;
                }

                if (this.UseSalt)
                    salt = GenerateSalt();

                pass = EncodeString(password, salt, this.UseSalt, this.HashHex, this.HashCase, this.HashRemoveChars, true);

                //// Encode Password Answer
                string encodedPasswordAnswer = EncodeString(passwordAnswer, salt, this.UseSalt, this.HashHex, this.HashCase, this.HashRemoveChars, true);

                // Process database user creation request
                string activationCode = Hanhud.Utils.CHelper.GeneratePassword(10);

                SqlCommand cmd = new SqlCommand(_db.GetObjectName("CreateUser"));
                cmd.CommandType = CommandType.StoredProcedure;

                cmd.Parameters.Add("@ActivationCode", SqlDbType.NVarChar, 10).Value = activationCode;
                cmd.Parameters.Add("@UserName", SqlDbType.NVarChar, 50).Value = username;
                cmd.Parameters.Add("@Password", SqlDbType.NVarChar, 128).Value = pass;
                cmd.Parameters.Add("@Email", SqlDbType.NVarChar, 256).Value = email;
                cmd.Parameters.Add("@PasswordQuestion", SqlDbType.NVarChar, 256).Value = passwordQuestion;
                cmd.Parameters.Add("@PasswordAnswer", SqlDbType.NVarChar, 256).Value = passwordAnswer;
                cmd.Parameters.Add("@IsApproved", SqlDbType.Bit).Value = isApproved;
                cmd.Parameters.Add("@CurrentTimeUtc", SqlDbType.DateTime).Value = DateTime.UtcNow;
                cmd.Parameters.Add("@CreateDate", SqlDbType.DateTime).Value = Hanhud.Utils.CHelper.GetFullDbDateTimeFormat(DateTime.Now);
                cmd.Parameters.Add("@UniqueEmail", SqlDbType.Bit).Value = true;
                cmd.Parameters.Add("@UserId", SqlDbType.UniqueIdentifier).Direction = ParameterDirection.Output;
                cmd.Parameters.Add("@RETURN_VALUE", SqlDbType.Int).Direction = ParameterDirection.ReturnValue;

                int affectedRows = _db.ExecuteNonQuery(cmd);
                int retv = Convert.ToInt32(cmd.Parameters["@RETURN_VALUE"].Value);

                CMembershipUser newUser = null;

                if (retv == 0)
                {
                    createStatus = EMembershipCreateStatus.Success;
                    newUser = GetUser(username, false);
                    newUser.UserName = username;
                }
                else
                {
                    try
                    {
                        createStatus = (EMembershipCreateStatus)Convert.ToInt32(retv);
                    }
                    catch (Exception)
                    {
                        createStatus = EMembershipCreateStatus.ProviderError;
                    }
                }
                return newUser;
            }

            internal bool DeleteUser(string username, bool deleteAllRelatedData)
            {
                throw new NotImplementedException();
            }

            internal CMembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
            {
                throw new NotImplementedException();
            }

            internal CMembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
            {
                throw new NotImplementedException();
            }

            internal CMembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
            {
                if (pageIndex < 0) throw new ArgumentOutOfRangeException("pageIndex must be greater than 0");
                if (pageSize < 1) throw new ArgumentOutOfRangeException("pageSize must be greater than 1");

                //CMembershipUserCollection users = new CMembershipUserCollection();
                System.Collections.Generic.List<CMembershipUser> list = new System.Collections.Generic.List<CMembershipUser>();

                SqlCommand cmd = new SqlCommand(_db.GetObjectName("GetAllUsers"));
                cmd.CommandType = CommandType.StoredProcedure;

                cmd.Parameters.Add("@PageIndex", SqlDbType.Int).Value = pageIndex;
                cmd.Parameters.Add("@PageSize", SqlDbType.Int).Value = pageSize;
                cmd.Parameters.Add("@RETURN_VALUE", SqlDbType.Int).Direction = ParameterDirection.ReturnValue;

                using (CDbConnectionManager m = _db.GetConnectionManager())
                {
                    cmd.Connection = m.OpenDBConnection;
                    IDataReader reader = _db.ExecuteReader(cmd, CommandBehavior.SingleResult);
                    while (reader.Read())
                    {
                        list.Add(GetMembershipUserFromReader(reader));
                    }

                    reader.Close();
                }
                totalRecords = Convert.ToInt32(cmd.Parameters["@RETURN_VALUE"].Value);

                return new CMembershipUserCollection(list.ToArray());
            }

            internal int GetNumberOfUsersOnline()
            {
                throw new NotImplementedException();
            }

            internal string GetPassword(string username, string answer)
            {
                throw new NotImplementedException();
            }

            internal CMembershipUser GetUser(string username, bool userIsOnline)
            {
                if (string.IsNullOrEmpty(username)) throw new ArgumentException("UserName is an empty string");

                SqlCommand cmd = new SqlCommand(_db.GetObjectName("GetUserByName"));
                cmd.CommandType = CommandType.StoredProcedure;

                CMembershipUser currentUser = null;

                cmd.Parameters.Add("@UserName", SqlDbType.NVarChar, 50).Value = username;
                cmd.Parameters.Add("@CurrentTimeUtc", SqlDbType.DateTime).Value = DateTime.UtcNow;
                cmd.Parameters.Add("@UpdateLastActivity", SqlDbType.Bit).Value = userIsOnline;
                cmd.Parameters.Add("@RETURN_VALUE", SqlDbType.Int).Direction = ParameterDirection.ReturnValue;

                using (CDbConnectionManager m = _db.GetConnectionManager())
                {
                    cmd.Connection = m.OpenDBConnection;
                    IDataReader reader = _db.ExecuteReader(cmd, CommandBehavior.SingleRow);

                    if (reader.Read())
                    {
                        currentUser = GetMembershipUserFromReader(reader, userIsOnline);
                        currentUser.UserName = username;
                    }
                    reader.Close();
                }

                return currentUser;
            }

            internal CMembershipUser GetUser(object providerUserKey, bool userIsOnline)
            {
                throw new NotImplementedException();
            }

            internal string GetUserNameByEmail(string email)
            {
                if (string.IsNullOrEmpty(email)) throw new ArgumentException("Email is an empty string");

                string userName = null;
                SqlCommand cmd = new SqlCommand(_db.GetObjectName("GetUserNameByEmail"));
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.Add("@Email", SqlDbType.NVarChar, 256).Value = email;
                object o = _db.ExecuteSalar(cmd);
                if (o != null) userName = o.ToString();
                return userName;
            }

            internal string ResetPassword(string username, string answer)
            {
                throw new NotImplementedException();
            }

            internal bool UnlockUser(string userName)
            {
                throw new NotImplementedException();
            }

            internal void UpdateUser(CMembershipUser user)
            {
                throw new NotImplementedException();
            }

            internal bool ValidateUser(string username, string password)
            {
                SqlCommand cmd = new SqlCommand(_db.GetObjectName("ValidateUser"));
                cmd.CommandType = CommandType.StoredProcedure;

                cmd.Parameters.Add("@UserName", SqlDbType.NVarChar, 50).Value = username;
                cmd.Parameters.Add("@Password", SqlDbType.NVarChar, 128).Value = EncodeString(password, string.Empty, this.UseSalt, this.HashHex, this.HashCase, this.HashRemoveChars, true);
                cmd.Parameters.Add("@RETURN_VALUE", SqlDbType.Int).Direction = ParameterDirection.ReturnValue;

                int affectedRows = _db.ExecuteNonQuery(cmd);
                //cmd.Connection.Close();
                int retv = Convert.ToInt32(cmd.Parameters["@RETURN_VALUE"].Value);

                return retv == 1;
            }
            #endregion

            #region Private Methods
            private bool IsPasswordCompliant(string password)
            {
                return IsPasswordCompliant(password, MinRequiredPasswordLength, MinRequiredNonAlphanumericCharacters, PasswordStrengthRegularExpression);
            }
            #endregion

            #region Private Static Methods
            private static bool IsPasswordCompliant(string password, int minLength, int minNonAlphaNumerics, string strengthRegEx)
            {
                // Check password meets minimum length criteria.
                if (!(password.Length >= minLength))
                {
                    return false;
                }

                // Count Non alphanumerics
                int symbolCount = 0;
                foreach (char checkChar in password.ToCharArray())
                {
                    if (!char.IsLetterOrDigit(checkChar))
                    {
                        symbolCount++;
                    }
                }

                // Check password meets minimum alphanumeric criteria
                if (!(symbolCount >= minNonAlphaNumerics))
                {
                    return false;
                }

                // Check Reg Expression is present
                if (strengthRegEx != null && strengthRegEx.Length > 0)
                {
                    // Check password strength meets Password Strength Regex Requirements
                    if (!Regex.IsMatch(password, strengthRegEx))
                    {
                        return false;
                    }
                }

                // Check string meets requirements as set in config
                return true;
            }
            /// <summary>
            /// The to hex string.
            /// </summary>
            /// <param roomID="hashedBytes">
            /// The hashed bytes.
            /// </param>
            /// <returns>
            /// The to hex string.
            /// </returns>
            private static string ToHexString(byte[] hashedBytes)
            {
                var hashedSB = new StringBuilder(hashedBytes.Length * 2 + 2);
                foreach (byte b in hashedBytes)
                {
                    hashedSB.AppendFormat("{0:X2}", b);
                }

                return hashedSB.ToString();
            }
            /// <summary>
            /// Creates a random string used as Salt for hashing
            /// </summary>
            /// <returns>
            /// Random string
            /// </returns>
            private static string GenerateSalt()
            {
                var buf = new byte[16];
                var rngCryptoSp = new RNGCryptoServiceProvider();
                rngCryptoSp.GetBytes(buf);
                return Convert.ToBase64String(buf);
            }

            /// <summary>
            /// Creates a random password based on a miniumum length and a minimum number of non-alphanumeric characters
            /// </summary>
            /// <param roomID="minPassLength">
            /// Minimum characters in the password
            /// </param>
            /// <param roomID="minNonAlphas">
            /// Minimum non-alphanumeric characters
            /// </param>
            /// <returns>
            /// Random string
            /// </returns>
            private static string GeneratePassword(int minPassLength, int minNonAlphas)
            {
                return Hanhud.Utils.CHelper.GeneratePassword(minPassLength < Core.CBaseConfig.Current.MembershipSection.MinPasswordLength ? Core.CBaseConfig.Current.MembershipSection.MinPasswordLength : minPassLength, Core.CBaseConfig.Current.MembershipSection.MaxPasswordLength);
            }

            /// <summary>
            /// The hash type.
            /// </summary>
            /// <returns>
            /// The hash type.
            /// </returns>
            private static string HashType()
            {
                return Core.CBaseConfig.Current.MembershipSection.HashAlgorithmType;
            }

            /// <summary>
            /// Hashes a clear string to the given hashtype
            /// </summary>
            /// <param roomID="clearString">
            /// Clear string to hash
            /// </param>
            /// <param roomID="hashType">
            /// hash Algorithm to be used
            /// </param>
            /// <param roomID="salt">
            /// Salt to be applied to hashing algorithm
            /// </param>
            /// <param roomID="useSalt">
            /// Should salt be applied to hashing algorithm
            /// </param>
            /// <param roomID="hashHex">
            /// The hash Hex.
            /// </param>
            /// <param roomID="hashCase">
            /// The hash Case.
            /// </param>
            /// <param roomID="hashRemoveChars">
            /// The hash Remove Chars.
            /// </param>
            /// <param roomID="standardComp">
            /// The standard Comp.
            /// </param>
            /// <returns>
            /// Hashed String as Hex or Base64 
            /// </returns>
            internal static string Hash(
              string clearString, string hashType, string salt, bool useSalt, bool hashHex, string hashCase, string hashRemoveChars, bool standardComp)
            {
                byte[] buffer;
                if (useSalt)
                {
                    buffer = GeneratePasswordBuffer(salt, clearString, standardComp);
                }
                else
                {
                    byte[] unencodedBytes = Encoding.UTF8.GetBytes(clearString); // UTF8 used to maintain compatibility
                    buffer = new byte[unencodedBytes.Length];
                    Buffer.BlockCopy(unencodedBytes, 0, buffer, 0, unencodedBytes.Length);
                }

                byte[] hashedBytes = Hash(buffer, hashType); // Hash

                string hashedString;

                if (hashHex)
                {
                    hashedString = Hanhud.Utils.CHelper.ToHexString(hashedBytes);
                }
                else
                {
                    hashedString = Convert.ToBase64String(hashedBytes);
                }

                // Adjust the case of the hash output
                switch (hashCase.ToLower())
                {
                    case "upper":
                        hashedString = hashedString.ToUpper();
                        break;
                    case "lower":
                        hashedString = hashedString.ToLower();
                        break;
                }

                if (!String.IsNullOrEmpty(hashRemoveChars))
                {
                    foreach (char removeChar in hashRemoveChars)
                    {
                        hashedString = hashedString.Replace(removeChar.ToString(), string.Empty);
                    }
                }

                return hashedString;
            }
            #endregion

            #region Encryption Utilities

            /// <summary>
            /// Hashes clear bytes to given hashtype
            /// </summary>
            /// <param roomID="clearBytes">
            /// Clear bytes to hash
            /// </param>
            /// <param roomID="hashType">
            /// hash Algorithm to be used
            /// </param>
            /// <returns>
            /// Hashed bytes 
            /// </returns>
            private static byte[] Hash(byte[] clearBytes, string hashType)
            {
                // MD5, SHA1, SHA256, SHA384, SHA512
                byte[] hash = HashAlgorithm.Create(hashType).ComputeHash(clearBytes);
                return hash;
            }

            /// <summary>
            /// Creates a password buffer from salt and password ready for hashing/encrypting
            /// </summary>
            /// <param roomID="salt">
            /// Salt to be applied to hashing algorithm
            /// </param>
            /// <param roomID="clearString">
            /// Clear string to hash
            /// </param>
            /// <param roomID="standardComp">
            /// Use Standard asp.net membership method of creating the buffer
            /// </param>
            /// <returns>
            /// Salted Password as Byte Array 
            /// </returns>
            internal static byte[] GeneratePasswordBuffer(string salt, string clearString, bool standardComp)
            {
                byte[] unencodedBytes = Encoding.Unicode.GetBytes(clearString);
                byte[] saltBytes = Convert.FromBase64String(salt);
                var buffer = new byte[unencodedBytes.Length + saltBytes.Length];

                if (standardComp)
                {
                    // Compliant with ASP.NET Membership method of hash/salt
                    Buffer.BlockCopy(saltBytes, 0, buffer, 0, saltBytes.Length);
                    Buffer.BlockCopy(unencodedBytes, 0, buffer, saltBytes.Length, unencodedBytes.Length);
                }
                else
                {
                    Buffer.BlockCopy(unencodedBytes, 0, buffer, 0, unencodedBytes.Length);
                    Buffer.BlockCopy(saltBytes, 0, buffer, unencodedBytes.Length - 1, saltBytes.Length);
                }

                return buffer;
            }

            /// <summary>
            /// The encrypt.
            /// </summary>
            /// <param roomID="clearString">
            /// The clear string.
            /// </param>
            /// <param roomID="saltString">
            /// The salt string.
            /// </param>
            /// <param roomID="standardComp">
            /// The standard comp.
            /// </param>
            /// <returns>
            /// The encrypt.
            /// </returns>
            private static string Encrypt(string clearString, string saltString, bool standardComp)
            {
                byte[] buffer = GeneratePasswordBuffer(saltString, clearString, standardComp);
                return Convert.ToBase64String(Activator.CreateInstance<CMembershipProvider>().EncryptPassword(buffer));
            }

            private byte[] EncryptPassword(byte[] buffer)
            {
                throw new NotImplementedException();
            }

            #endregion

            /// <summary>
            /// Encrypt string to hash method.
            /// </summary>
            /// <param roomID="clearString">
            /// UnEncrypted Clear String
            /// </param>
            /// <param roomID="encFormat">
            /// The enc Format.
            /// </param>
            /// <param roomID="salt">
            /// Salt to be used in Hash method
            /// </param>
            /// <param roomID="useSalt">
            /// Salt to be used in Hash method
            /// </param>
            /// <param roomID="hashHex">
            /// The hash Hex.
            /// </param>
            /// <param roomID="hashCase">
            /// The hash Case.
            /// </param>
            /// <param roomID="hashRemoveChars">
            /// The hash Remove Chars.
            /// </param>
            /// <param roomID="msCompliant">
            /// The ms Compliant.
            /// </param>
            /// <returns>
            /// Encrypted string
            /// </returns>
            internal static string EncodeString(
              string clearString, string salt, bool useSalt, bool hashHex, string hashCase, string hashRemoveChars, bool msCompliant)
            {
                string encodedPass = string.Empty;

                //var passwordFormat = (MembershipPasswordFormat)Enum.ToObject(typeof(MembershipPasswordFormat), encFormat);

                // Multiple Checks to ensure UseSalt is valid.

                if (String.IsNullOrEmpty(clearString))
                {
                    // Check to ensure string is not null or empty.
                    return String.Empty;
                }

                if (useSalt && String.IsNullOrEmpty(salt))
                {
                    // If Salt value is null disable Salt procedure
                    useSalt = false;
                }

                return Hash(clearString, HashType(), salt, useSalt, hashHex, hashCase, hashRemoveChars, msCompliant);
            }

            protected virtual CMembershipUser GetMembershipUserFromReader(IDataReader reader)
            {
                return new CMembershipUser(
                    reader["ActivationCode"].ToString(),
                    new Guid(reader["UserId"].ToString()),
                    reader["Email"].ToString(),
                    reader["PasswordQuestion"] == DBNull.Value ? string.Empty : reader["PasswordQuestion"].ToString(),
                    Convert.ToBoolean(reader["IsApproved"]),
                    Convert.ToBoolean(reader["IsLockedOut"]),
                    Convert.ToDateTime(reader["CreatedDate"]),
                    Convert.ToDateTime(reader["LastLoginDate"]),
                    Convert.ToDateTime(reader["LastActivityDate"]),
                    Convert.ToDateTime(reader["LastPasswordChangedDate"]),
                    Convert.ToDateTime(reader["LastLockoutDate"])
                    );
            }

            protected virtual CMembershipUser GetMembershipUserFromReader(IDataReader reader, bool userIsOnline)
            {
                return new CMembershipUser(
                    reader["ActivationCode"].ToString(),
                    new Guid(reader["UserId"].ToString()),
                    reader["Email"].ToString(),
                    reader["PasswordQuestion"] == DBNull.Value ? string.Empty : reader["PasswordQuestion"].ToString(),
                    Convert.ToBoolean(reader["IsApproved"]),
                    Convert.ToBoolean(reader["IsLockedOut"]),
                    Convert.ToDateTime(reader["CreatedDate"]),
                    Convert.ToDateTime(reader["LastLoginDate"]),
                    Convert.ToDateTime(reader["LastActivityDate"]),
                    Convert.ToDateTime(reader["LastPasswordChangedDate"]),
                    Convert.ToDateTime(reader["LastLockoutDate"]),
                    userIsOnline
                    );
            }
        }
    }
}
